storage.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import dayjs from 'dayjs'
  2. import { ArrowRight, ExternalLinkIcon, RefreshCw } from 'lucide-react'
  3. import Link from 'next/link'
  4. import {
  5. NetworkTrafficRenderer,
  6. ResponseSpeedChartRenderer,
  7. TopApiRoutesRenderer,
  8. TotalRequestsChartRenderer,
  9. } from '@/components/interfaces/Reports/renderers/ApiRenderers'
  10. import {
  11. CacheHitRateChartRenderer,
  12. TopCacheMissesRenderer,
  13. } from '@/components/interfaces/Reports/renderers/StorageRenderers'
  14. import ReportFilterBar from '@/components/interfaces/Reports/ReportFilterBar'
  15. import ReportHeader from '@/components/interfaces/Reports/ReportHeader'
  16. import ReportPadding from '@/components/interfaces/Reports/ReportPadding'
  17. import { REPORT_DATERANGE_HELPER_LABELS } from '@/components/interfaces/Reports/Reports.constants'
  18. import ReportStickyNav from '@/components/interfaces/Reports/ReportStickyNav'
  19. import ReportWidget from '@/components/interfaces/Reports/ReportWidget'
  20. import {
  21. DatePickerValue,
  22. LogsDatePicker,
  23. } from '@/components/interfaces/Settings/Logs/Logs.DatePickers'
  24. import UpgradePrompt from '@/components/interfaces/Settings/Logs/UpgradePrompt'
  25. import DefaultLayout from '@/components/layouts/DefaultLayout'
  26. import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/ObservabilityLayout'
  27. import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
  28. import { ObservabilityLink } from '@/components/ui/ObservabilityLink'
  29. import { useStorageReport } from '@/data/reports/storage-report-query'
  30. import { useRefreshHandler, useReportDateRange } from '@/hooks/misc/useReportDateRange'
  31. import { DOCS_URL } from '@/lib/constants'
  32. import type { NextPageWithLayout } from '@/types'
  33. export const StorageReport: NextPageWithLayout = () => {
  34. const report = useStorageReport()
  35. const {
  36. data,
  37. error,
  38. filters,
  39. isLoading,
  40. params,
  41. mergeParams,
  42. removeFilters,
  43. addFilter,
  44. refresh,
  45. } = report
  46. const {
  47. datePickerHelpers,
  48. datePickerValue,
  49. handleDatePickerChange: handleDatePickerChangeFromHook,
  50. showUpgradePrompt,
  51. setShowUpgradePrompt,
  52. selectedDateRange,
  53. } = useReportDateRange(REPORT_DATERANGE_HELPER_LABELS.LAST_60_MINUTES)
  54. const handleDatepickerChange = (vals: DatePickerValue) => {
  55. const promptShown = handleDatePickerChangeFromHook(vals)
  56. if (!promptShown) {
  57. mergeParams({
  58. iso_timestamp_start: vals.from ?? '',
  59. iso_timestamp_end: vals.to ?? '',
  60. })
  61. }
  62. }
  63. const onRefreshReport = useRefreshHandler(
  64. datePickerValue,
  65. datePickerHelpers,
  66. handleDatepickerChange,
  67. refresh
  68. )
  69. return (
  70. <ReportPadding>
  71. <ReportHeader title="Storage" showDatabaseSelector={false} />
  72. <ReportStickyNav
  73. content={
  74. <div className="flex flex-col gap-2">
  75. <div className="flex items-center gap-2">
  76. <ButtonTooltip
  77. type="default"
  78. disabled={report.isLoading}
  79. icon={<RefreshCw className={report.isLoading ? 'animate-spin' : ''} />}
  80. className="w-7"
  81. tooltip={{ content: { side: 'bottom', text: 'Refresh report' } }}
  82. onClick={onRefreshReport}
  83. />
  84. <LogsDatePicker
  85. onSubmit={handleDatepickerChange}
  86. value={datePickerValue}
  87. helpers={datePickerHelpers}
  88. />
  89. {selectedDateRange && (
  90. <div className="flex items-center gap-x-2 text-xs">
  91. <p className="text-foreground-light">
  92. {dayjs(selectedDateRange.period_start.date).format('MMM D, h:mma')}
  93. </p>
  94. <p className="text-foreground-light">
  95. <ArrowRight size={12} />
  96. </p>
  97. <p className="text-foreground-light">
  98. {dayjs(selectedDateRange.period_end.date).format('MMM D, h:mma')}
  99. </p>
  100. </div>
  101. )}
  102. <UpgradePrompt
  103. show={showUpgradePrompt}
  104. setShowUpgradePrompt={setShowUpgradePrompt}
  105. title="Report date range"
  106. description="Report data can be stored for a maximum of 3 months depending on the plan that your project is on."
  107. source="storageReportDateRange"
  108. />
  109. </div>
  110. <ReportFilterBar
  111. onRemoveFilters={removeFilters}
  112. onDatepickerChange={handleDatepickerChange}
  113. datepickerFrom={params.totalRequests.iso_timestamp_start}
  114. datepickerTo={params.totalRequests.iso_timestamp_end}
  115. onAddFilter={addFilter}
  116. isLoading={isLoading}
  117. filters={filters}
  118. selectedProduct="storage"
  119. hideDatepicker={true}
  120. datepickerHelpers={datePickerHelpers}
  121. initialDatePickerValue={datePickerValue}
  122. className="w-full"
  123. showDatabaseSelector={false}
  124. />
  125. </div>
  126. }
  127. >
  128. <div className="mt-8 flex flex-col gap-4">
  129. <ReportWidget
  130. isLoading={isLoading}
  131. params={params.totalRequests}
  132. title="Total Requests"
  133. data={data.totalRequests || []}
  134. error={error.totalRequest}
  135. renderer={TotalRequestsChartRenderer}
  136. append={TopApiRoutesRenderer}
  137. appendProps={{ data: data.topRoutes || [], params: params.topRoutes }}
  138. />
  139. <ReportWidget
  140. isLoading={isLoading}
  141. params={params.responseSpeed}
  142. title="Response Speed"
  143. tooltip="Average response speed of a request (in ms)"
  144. data={data.responseSpeed || []}
  145. error={error.responseSpeed}
  146. renderer={ResponseSpeedChartRenderer}
  147. appendProps={{ data: data.topSlowRoutes || [], params: params.topSlowRoutes }}
  148. append={TopApiRoutesRenderer}
  149. />
  150. <ReportWidget
  151. isLoading={isLoading}
  152. params={params.networkTraffic}
  153. error={error.networkTraffic}
  154. title="Network Traffic"
  155. tooltip="Ingress and egress of requests and responses respectively"
  156. data={data.networkTraffic || []}
  157. renderer={NetworkTrafficRenderer}
  158. />
  159. <ReportWidget
  160. isLoading={isLoading}
  161. params={params.cacheHitRate}
  162. title="Request Caching"
  163. tooltip={
  164. <div>
  165. The number of storage requests that are cached at the edge level. A higher number of
  166. hits is better.{' '}
  167. <span className="flex items-center gap-1 text-foreground-lighter">
  168. <Link href={`${DOCS_URL}/guides/storage/cdn/fundamentals`} target="_blank">
  169. Read More
  170. </Link>
  171. <ExternalLinkIcon className="w-3 h-3" />
  172. </span>
  173. </div>
  174. }
  175. data={data.cacheHitRate || []}
  176. renderer={CacheHitRateChartRenderer}
  177. append={TopCacheMissesRenderer}
  178. appendProps={{ data: data.topCacheMisses || [] }}
  179. />
  180. </div>
  181. </ReportStickyNav>
  182. <div className="py-8">
  183. <ObservabilityLink />
  184. </div>
  185. </ReportPadding>
  186. )
  187. }
  188. StorageReport.getLayout = (page) => (
  189. <DefaultLayout>
  190. <ObservabilityLayout title="Storage">{page}</ObservabilityLayout>
  191. </DefaultLayout>
  192. )
  193. export default StorageReport